/*--
 * Sum.cs: Calculates checksums of files using standard algorithms.
 *--
 * For more information, please visit the Metashell Project site:
 *   http://code.google.com/p/metashell/
 *--
 * (c) 2006 Christopher E. Granade.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; exactly version 2
 * of the License applies to this software.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
using Metashell.Core;
using Metashell.Core.Utility;

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;

namespace Metashell.Commands.Crypto {

	class Sum :ICommand {
	
		public static string NamespaceURI =
			MetadocNamespaces.Sums;
	
		private Metashell.Core.Core core;
		
		private Dictionary<string, Type> digests =
			new Dictionary<string, Type>();
		
		public string name {
			get { return "sum"; }
		}
		
		public void RegisterCore(Metashell.Core.Core core_) {
			core = core_;
		}
		
		public Sum() {
			
			// Build a dictionary of digest algorithms.
			digests.Add("sha224", typeof(Sha224Digest));
			digests.Add("sha256", typeof(Sha256Digest));
			digests.Add("sha384", typeof(Sha384Digest));
			digests.Add("sha512", typeof(Sha512Digest));
			digests.Add("sha1", typeof(Sha1Digest));
			digests.Add("md5", typeof(MD5Digest));
			digests.Add("ripemd128", typeof(RipeMD128Digest));
			digests.Add("ripemd160", typeof(RipeMD160Digest));
			digests.Add("ripemd256", typeof(RipeMD256Digest));
			digests.Add("ripemd320", typeof(RipeMD320Digest));
			digests.Add("tiger", typeof(TigerDigest));
			digests.Add("whirlpool", typeof(WhirlpoolDigest));
			
		}
	
		private string Hash(ref byte[] buffer, string alg) {
			
			// Allocate a generic digest interface.
			Org.BouncyCastle.Crypto.IDigest gd =
				(IDigest)System.Activator.CreateInstance(digests[alg]);
			
			// OK, calculate the digest.
			byte[] digest = new byte[gd.GetDigestSize()];
			gd.BlockUpdate(buffer, 0, buffer.Length);
			gd.DoFinal(digest, 0);
			
			// Convert the digest to a hex representation.
			return Strings.BytesToHex(digest);
			
		}
	
	
		public void Run(
			string parameters,
			Metadoc indoc,
			out Metadoc outdoc
		) {
			
			// Break into arguments and options.
			List<string> args = Strings.ArgsFromParams(parameters);
			List<string> opts = Strings.OptsFromParams(parameters);
			
			// Check arguments length.
			if (args.Count < 1) {
			
				core.RaiseError("Usage: sum --algorithm file");
			
				outdoc = XML.NullDocument;
				
				return;
				
			}
			
			// Check if file is in local directory first.
			string truefile =
				core.CurrentDirectory + Path.DirectorySeparatorChar + args[0];
			
			if (!File.Exists(truefile)) {
				
				// Oh, well. Treat as absolute.
				truefile = args[0];
				
			}
			
			// Open the file.
			FileStream fs = File.Open(truefile, FileMode.Open);
			byte[] buffer = new byte[fs.Length];
			
			fs.Read(buffer, 0, (int)fs.Length);
			fs.Close();
			
			// Start the document.
			outdoc.format = MetadocFormats.Sums;
			outdoc.data = new XmlDocument();
			
			XmlElement root = outdoc.data.CreateElement("s", "sums", NamespaceURI);
			outdoc.data.AppendChild(root);
			
			XmlElement file = outdoc.data.CreateElement("s", "file", NamespaceURI);
			root.AppendChild(file);
			
			file.SetAttribute("name", NamespaceURI, truefile);
			
			// Now get all the hashes.
			foreach (string opt in opts) {
				if (digests.ContainsKey(opt)) {
					XmlElement sum =
						outdoc.data.CreateElement("s", "sum", NamespaceURI);
						
					sum.SetAttribute("algorithm", NamespaceURI, opt);
					sum.InnerText = Hash(ref buffer, opt);
					
					file.AppendChild(sum);
				}
			}
			
			
		}
		
	}
	
}